﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Web.Caching;
using System.Web.UI;
using AWS.SLAM.Activation;
using AWS.SLAM.Configuration;
using AWS.SLAM.Events;
using AWS.SLAM.Logging;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Web;

namespace AWS.SLAM.Administration
{
    public partial class Synchronization : System.Web.UI.Page, ICallbackEventHandler
    {
        private int timeout;
        private string slamFolder = "";

        protected override void OnInit(EventArgs e)
        {
            timeout = Server.ScriptTimeout;
            Server.ScriptTimeout = 8000;
            base.OnInit(e);
        }

        protected override void OnUnload(EventArgs e)
        {
            Server.ScriptTimeout = timeout;
            base.OnUnload(e);
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            string synchTypeName = "";
            string synchTypeId = "";
            string synchSite = "";
            string synchSiteCollection = "";

            if (Request.QueryString["typename"] != null)
            {
                synchTypeName = Request.QueryString["typename"];
            }

            if (Request.QueryString["typeid"] != null)
            {
                synchTypeId = Request.QueryString["typeid"];
            }

            if (Request.QueryString["site"] != null)
            {
                synchSite = HttpUtility.UrlDecode(Request.QueryString["site"]);
            }

            if (Request.QueryString["sitecollection"] != null)
            {
                synchSiteCollection = HttpUtility.UrlDecode(Request.QueryString["sitecollection"]);
            }

            //if under a site collection other than the root, doing a simple
            //redirect to the page will always redirect to the root, so the desired
            //location needs extracted from the RawUrl
            slamFolder = Request.RawUrl.Substring(0, Request.RawUrl.LastIndexOf("/") + 1);

            if (Request["__EVENTARGUMENT"] == "doredirect")
            {
                Response.Redirect(slamFolder + "Diagnostics.aspx");
            }

            if (!IsPostBack)
            {
                //Test if the 3.5 Framework is installed
                if (!InstallationValidator.VerifyNetFx35IsInstalled())
                {
                    Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
                }
                else
                {
                    //Test if SLAM can clear instances, if it cannot go back to the Control Panel
                    try
                    {
                        ComponentService.ClearInstances();
                    }
                    catch
                    {
                        Response.Redirect(slamFolder + "SLAMControlPanel.aspx");
                    }

                    hdnSessionKey.Value = DateTime.Now.Ticks.ToString();

                    SPSite site = SPControl.GetContextSite(System.Web.HttpContext.Current);
                    SynchronizationProcess process = new SynchronizationProcess(DoSynchronization);

                    Cache.Insert("syncresult" + hdnSessionKey.Value.ToString(), process.BeginInvoke(site, synchTypeName, synchTypeId, synchSiteCollection, synchSite, new AsyncCallback(CallBackMethod), null), null, DateTime.Now.AddHours(2), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);


                    //Register javascript functions to handle synch progress updating
                    //The process is started with a setTimeout to the intial GetStatus because
                    //in after a app pool recycle in IE this process is not initiated every time
                    //unless it waits for a moment, so the wait always occurs before the first
                    //update.
                    ClientScript.RegisterStartupScript(GetType(), "StartCallbackScript", @" 
                        function GetStatus()
                        {   
                            " + ClientScript.GetCallbackEventReference(this, "", "ShowStatus", null) + @";
                        }
                        function ShowStatus(eventArgument,context)
                        {   
                            document.getElementById('progressArea').innerHTML = eventArgument;
                            if(eventArgument == ""Finishing..."") 
                            {
                                setTimeout(""Finish()"", 1500);
                            }
                            else 
                            {
                                setTimeout(""GetStatus()"", 1000); 
                            }
                        }
                        function Finish() 
                        {   
                            " + ClientScript.GetPostBackEventReference(this, "doredirect", false) + @";
                        }
                        setTimeout(""GetStatus()"", 300); ", true);
                }
            }

            
        }

        private void CallBackMethod(IAsyncResult ar)
        {
            AsyncResult Result = (AsyncResult)ar;
            SynchronizationProcess process = (SynchronizationProcess)Result.AsyncDelegate;
            process.EndInvoke(Result);
        }

        private delegate void SynchronizationProcess(SPSite site, string typeName, string typeId, string typeSiteCollection, string typeSite);

        private void DoSynchronization(SPSite site, string typeName, string typeId, string typeSiteCollection, string typeSite)
        {
            IConfigurationManager config = ComponentService.GetInstance<IConfigurationManager>();
            EventUtility eventUtility = new EventUtility();
            eventUtility.DisableEventHandlers();
            
            ILogger logger = ComponentService.GetInstance<ILogger>();
            TraceLevel currentTraceLevel = TraceLevel.Error;
            
            SqlLogger sqlLogger = logger is SqlLogger ? ((SqlLogger)logger) : null;
            if (sqlLogger != null)
            {
                currentTraceLevel = sqlLogger.Switch.Level;
                sqlLogger.Switch.Level = TraceLevel.Verbose;
            }
            logger.LogMessage("Information", "Data Synchronization initiated");
            if (sqlLogger != null)
            {
                sqlLogger.Switch.Level = currentTraceLevel;
            }

            string siteCollection = site.ServerRelativeUrl.TrimStart('/');
            DataMapping currentDataMapping = config.DataMapping;
            if (!String.IsNullOrEmpty(siteCollection) && config.DataMappings.ContainsKey(siteCollection))
            {
                currentDataMapping = config.DataMappings[siteCollection];
            }

            int currentTypeNumber = 1;
            Cache.Insert("syncstatus" + hdnSessionKey.Value.ToString(), "Initiating...", null, DateTime.Now.AddHours(2), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);

            bool runTypeSubset = false;
            if (!String.IsNullOrEmpty(typeName) || !String.IsNullOrEmpty(typeId) || !String.IsNullOrEmpty(typeSite) || !String.IsNullOrEmpty(typeSiteCollection))
            {
                runTypeSubset = true;
            }

            List<ConfiguredType> typesToSynch = new List<ConfiguredType>();

            foreach (ConfiguredType type in currentDataMapping.GetAllTypes())
            {
                if (!runTypeSubset)
                {
                    typesToSynch.Add(type);
                }
                else if (type.Name == typeName)
                {
                    typesToSynch.Add(type);
                }
            }

            int totalTypes = typesToSynch.Count;

            foreach (ConfiguredType type in typesToSynch)
            {
                string itemTypeName = type.Name;

                if (type is SqlConfiguredType)
                {
                    SqlConfiguredType sqlType = (SqlConfiguredType)type;
                    if (!String.IsNullOrEmpty(sqlType.Table))
                    {
                        itemTypeName = sqlType.Table;
                    }
                }

                Cache["syncstatus" + hdnSessionKey.Value.ToString()] = currentTypeNumber.ToString() + " of " + totalTypes.ToString() + "<br />Processing " + itemTypeName + "...";

                try
                {
                    IDataSynchronizeTypeProcessor processor = ComponentService.GetInstance<IDataSynchronizeTypeProcessor>(type.SiteCollection, type.Site, type.Id, itemTypeName);
                    processor.Process(site.Url, new Dictionary<string, object>() { { "ConfigType", type }, { "CreateTable", true }, { "PopulateData", true } });
                }
                catch (Exception ex)
                {
                    //Because this process is asynchronous, in case an unexpected exception is
                    //thrown while processing this type, catch it and log it
                    sqlLogger.LogMessage("Exception", ex.ToString());
                }
                if (sqlLogger != null)
                {   
                    sqlLogger.Switch.Level = TraceLevel.Verbose;
                }
                logger.LogMessage("Information", "Data Synchronization Processing: " + type.Name);
                if (sqlLogger != null)
                {
                    sqlLogger.Switch.Level = currentTraceLevel;
                }
                currentTypeNumber++;
                Cache["syncstatus" + hdnSessionKey.Value.ToString()] = currentTypeNumber.ToString() + " of " + totalTypes.ToString();
            }

            if (sqlLogger != null)
            {
                sqlLogger.Switch.Level = TraceLevel.Verbose;
            }
            logger.LogMessage("Information", "Data Synchronization Complete");
            if (logger is SqlLogger)
            {
                ((SqlLogger)logger).Switch.Level = currentTraceLevel;
            }

            eventUtility.EnableEventHandlers();
            Cache["syncstatus" + hdnSessionKey.Value.ToString()] = "Finishing...";
        }

        #region ICallbackEventHandler Members

        public string GetCallbackResult()
        {
            return Cache["syncstatus" + hdnSessionKey.Value.ToString()].ToString();
        }

        public void RaiseCallbackEvent(string eventArgument)
        {   
        }

        #endregion
    }
}
